Research Q:

To what degree do the variety of property revaluation cycles affect county distress rankings 

Preliminary Answer: Little

Exploration of County Tiers Generally

Tier are four elements that determine a County’s Tier, aka its Distress Rank

  1. Per Capita Taxible Property Value
  2. Per Capita Taxible Income
  3. Population Growth Rate
  4. Unemployment Rate

Each County is ranked from 1-100 on each of these elements, the sum of each County’s four rankings are then ranked to determine the County’s overall Distress Rank. The Fourty ‘Most Distressed’ Counties are those fourty with the lowest Distress Rank, these fourty are Called Tier-1 Counties. The Counties with Distress Rankings of 41-80 are called Tier-2 Counties. The Counties with Distress Rankings of 81-100 are called Tier-3 Counties.

All data is derived from the NC Department of Commerce Website and represents the six years of data since the formula change of 2018

The Appropriations Act of 2018 rewrote the formula which had been in affect since 2007. Between 2007-2018, County distress rank would be determined by the same steps listed above, but quite significant ‘adjustement factors’ were then applied to the date before calculating the Tiers. The adjustment factors were more in the way of brute force than subtle alterations: all ‘small counties’ (under 12,000), and all mid-size counties (less than 50,000 people) with a poverty rate greaty than 19%, were automatically tier 1.

According to the Department of Commerce, these adjustment factors in 2017 (the last year of their use) made for 32 counties that would have been in a different tier had the adjustment factors been eliminated a year before–Significant indeed.

SL2018-5 SECTION 15.2.(a) “Adjustment for Certain Small Counties. – Regardless of the actual development factor, any county that has a population of less than 12,000 shall automatically be ranked one of the 40 highest counties, any county that has a population of less than 50,000 shall automatically be ranked one of the 80 highest counties, and any county that has a population of less than 50,000 and more than nineteen percent (19%) of its population below the federal poverty level according to the most recent federal decennial census shall automatically be ranked one of the 40 highest counties.”

What is the importance of one’s County Tier?

A Counties Tier determines its elegibility for certain state programs, for others it does not affect its elegibility but it does determine the level of local ‘match’ for projects.

Notable Programs with County Tiers as a Factor include: XXXXXXXXXXXXX XXXXXXXXXXXXX

1. Per Capita Taxible Property Value

#Prop Value
TiersDF %>%
  group_by(COUNTY) %>%
  summarize(AverageProp = mean(AdjustedProperty)) %>%
  ggplot(aes(COUNTY, AverageProp))+
  geom_col()+
  ylab("Per Capita Property Averaged over sample")+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1, size = 5))

2. Per Capita Taxible Income

##Average Income
TiersDF %>%
  group_by(COUNTY) %>%
  summarise(AverageInc = mean(Median.Household.Income)) %>%
  ggplot(aes(COUNTY, AverageInc))+
    geom_col()+
    ylab("Median Household Income Averaged over sample")+
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1, size = 5))

3. Population Growth Rate

##Averaged Population Growth
TiersDF %>%
  group_by(COUNTY) %>%
  summarise(AverageGrowth = mean(Population.Growth)) %>%
  ggplot(aes(COUNTY, AverageGrowth))+
  geom_col()+
  ylab("Pop Growth Rate Averaged over sample")+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1, size = 5))

4. Unemployment Rate

##Averaged Unemp
TiersDF %>%
  group_by(COUNTY) %>%
  summarise(AveUnemp = mean(Unemployment)) %>%
  ggplot(aes(COUNTY, AveUnemp))+
  geom_col()+
  ylab("Unemployement Rate Averaged over sample")+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1, size = 5))

Background:

What are the revaluation cycles?

NCGS 105-286 requires every county to perform a countywide revaluation at least every 8 years. Many counties, however, do it much more frequently. (Burke is on a four year cycle, last completed in 2023)

#########PROPERTY
##Cycle Length
CycleLength <- TiersDF %>%
  group_by(COUNTY) %>%
  summarise(Cyclelength = (mean(NextReval)) - (mean(LatestReval)))

CycleLength %>% 
  group_by(Cyclelength) %>%
  ggplot(aes(Cyclelength))+
  geom_freqpoly(binwidth = .5)+
  ylab("Number of Counties with each CycleLength")

FYI: The average Cycle Lenth is 5.79

Corelation

There is not as much correlation as expected between a revaluation and property value. In Burke’s case, the slope is lower between the year prior to revaluation and the year of revaluation (2023), than between two years prior and one

##Burke's Graph
BurkeDF <- TiersDF %>%
  filter(COUNTY == "BURKE")
BurkeDF %>%
  ggplot(aes(year, AdjustedProperty))+
  geom_line()+
  geom_point()+
  geom_vline(data = BurkeDF, aes(xintercept = LatestReval))

All Counties with Revaluations in Recent Years Overlaid

##Overlaid Graphs
TiersDF %>%
  filter(LatestReval >2019) %>%
  ggplot(aes(year, AdjustedProperty, color = COUNTY))+
  geom_line()+
  geom_point()+
  facet_wrap(~LatestReval, nrow = 1)+
  theme(legend.position="none")+
  geom_vline(data = filter(TiersDF, LatestReval >2019), aes(xintercept = LatestReval))+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1, size = 12))

Each Reval Year’s Counties Side by Side with Revaluation Year Marked

2020

TiersDF %>%
  filter(LatestReval == 2020) %>%
  ggplot(aes(year, AdjustedProperty, color = COUNTY))+
  geom_line()+
  geom_point()+
  facet_wrap(~COUNTY)+
  theme(legend.position="none")+
  geom_vline(data = filter(TiersDF, LatestReval == 2020), aes(xintercept = LatestReval))

2021

TiersDF %>%
  filter(LatestReval == 2021) %>%
  ggplot(aes(year, AdjustedProperty, color = COUNTY))+
  geom_line()+
  geom_point()+
  facet_wrap(~COUNTY)+
  theme(legend.position="none")+
  geom_vline(data = filter(TiersDF, LatestReval == 2021), aes(xintercept = LatestReval))

2022

TiersDF %>%
  filter(LatestReval == 2022) %>%
  ggplot(aes(year, AdjustedProperty, color = COUNTY))+
  geom_line()+
  geom_point()+
  facet_wrap(~COUNTY)+
  theme(legend.position="none")+
  geom_vline(data = filter(TiersDF, LatestReval == 2022), aes(xintercept = LatestReval))

2023

TiersDF %>%
  filter(LatestReval == 2023) %>%
  ggplot(aes(year, AdjustedProperty, color = COUNTY))+
  geom_line()+
  geom_point()+
  facet_wrap(~COUNTY)+
  theme(legend.position="none")+
  geom_vline(data = filter(TiersDF, LatestReval == 2023), aes(xintercept = LatestReval))

The takeaway is that the occurance of a Revaluation does not neccessarily Predict a Jump at all

  1. Now let’s find ordinary variance in County Prop Rank year to year, vs. variance in County Prop Rank on reval years.
  • to contextually point 2, find ordinary variance in other 3 rankings
##creating new DF with diffPropRank
ChangeinPropRank <-  TiersDF %>%
  group_by(COUNTY) %>%
  arrange(year) %>%
  reframe(diff(PropRank))
ChangeinPropRank <- select(ChangeinPropRank, "diff(PropRank)")
colnames(ChangeinPropRank) <- "diffPropRank"

Changeable <- TiersDF %>%
  filter(year != 2019) %>%
  group_by(COUNTY) %>%
  arrange(year) %>%
  arrange(COUNTY)


##this is same but for total y-o-y value change
ChangeinPropVal <-  TiersDF %>%
  group_by(COUNTY) %>%
  arrange(year) %>%
  reframe(diff(AdjustedProperty))
ChangeinPropVal <- select(ChangeinPropVal, "diff(AdjustedProperty)")
colnames(ChangeinPropVal) <- "diffPropVal"

ChangedPropRank <- bind_cols(ChangeinPropRank, ChangeinPropVal, Changeable)

We can calculate the year over year difference in Property Value Ranking, and view this against the reval year.

This should show us if one is more likely to move up the ranks if they have a reval year

Here is what is Property Difference looks like with a sample county (Burke of course), compared against the reval year.

##What it Shows
ChangedPropRank %>%
  filter(COUNTY == "BURKE") %>%
  ggplot(aes(year, diffPropRank))+
  geom_line()+
  geom_vline(xintercept = 2023)

That is a significant Jump! Lets zoom out and see the trends more generally

Below is the Average year-to-year rank change when one has a revaluation; and below that is the aveerage year-to-year rank change when one does not have a revaluation

##This is big
avechangeonrevalyear <- ChangedPropRank %>%
  filter(LatestReval == year)
mean(avechangeonrevalyear$diffPropRank)
## [1] 0.08571429
avechangenotreval <- ChangedPropRank %>%
  filter(LatestReval != year)
mean(avechangenotreval$diffPropRank)
## [1] -0.01395349
###interesting

This tell us that on average a county moves up 0.08 ranks when revaluating; and they move down 0.01 ranks when not revaluating

It is now apparent there is a correlation between rankings and revaluations, but it is slight

Here is some further information on year-over-year change in Property Value Rankings

quantile(avechangenotreval$diffPropRank)
##   0%  25%  50%  75% 100% 
##  -23   -2    0    1   28
quantile(avechangeonrevalyear$diffPropRank)
##   0%  25%  50%  75% 100% 
##  -11   -1    0    1   14

The following graph, showing year over year Property Value Rank Change, overlaying each revaluation year, again shows that this correlation is not very large and not visually observable

ChangedPropRank %>%
  filter(LatestReval > 2019) %>%
  ggplot(aes(year, diffPropRank, color = COUNTY))+
  facet_wrap(~LatestReval)+
  geom_line()+
  geom_vline(aes(xintercept = `LatestReval`))+
  theme(legend.position="none")

Same scheme with total change in value

mean(avechangeonrevalyear$diffPropVal)
## [1] 5088.443
mean(avechangenotreval$diffPropVal)
## [1] 5501.516
quantile(avechangeonrevalyear$diffPropVal)
##      0%     25%     50%     75%    100% 
## -2179.0  2882.5  4500.0  5677.5 24568.0
quantile(avechangenotreval$diffPropVal)
##         0%        25%        50%        75%       100% 
## -183149.00    2016.25    4184.00    7327.00  237342.00

The outlier here is quite apparent. Further Research Q: Find out what happened in Hyde County

ChangedPropRank %>%
  filter(LatestReval > 2019) %>%
  ggplot(aes(year, diffPropVal, color = COUNTY))+
  facet_wrap(~LatestReval)+
  geom_line()+
  geom_vline(aes(xintercept = `LatestReval`))+
  theme(legend.position="none")

The first plot below shows Year-over-year variance in Property values when not in a Revaluation Year

avechangenotreval %>%
  ggplot(aes(diffPropVal))+
  geom_boxplot()

The next graph shows the same for Revaluation Years

avechangeonrevalyear %>%
  ggplot(aes(diffPropVal))+
  geom_boxplot()

I’ve again verified the Hyde County Data is accurate, though there does not appear to be any news coverage of the sudden, extreme change in value Lets do another box plot without Hyde county (the largest outlier by far), and normalize the scales

The first is non-revaluation years. The second is revaluation years

avechangenotreval %>%
  filter(COUNTY != "HYDE") %>%
  ggplot(aes(diffPropVal))+
  geom_boxplot()+
  scale_x_continuous(limits = c(-10000, 40000))
## Warning: Removed 1 rows containing non-finite values (`stat_boxplot()`).

 avechangeonrevalyear %>%
  ggplot(aes(diffPropVal))+
  geom_boxplot()+
  scale_x_continuous(limits = c(-10000, 40000))

Takeaway: Year-over-year change varies more widely in non-revaluation years. Also, it is more likely that a counties average property value will decrease in non-revaluation years.

Variance in rankings across the other 3 factors

      ## Lets see diff for other rankings too.
ChangeinIncomeRank <-  TiersDF %>%
  group_by(COUNTY) %>%
  arrange(year) %>%
  reframe(diff(IncomeRank))
ChangeinIncomeRank <- select(ChangeinIncomeRank, "diff(IncomeRank)")
colnames(ChangeinIncomeRank) <- "diffIncomeRank"


ChangeinPopRank <- TiersDF %>%
  group_by(COUNTY) %>%
  arrange(year) %>%
  reframe(diff(PopRank))
ChangeinPopRank <- select(ChangeinPopRank, "diff(PopRank)")
colnames(ChangeinPopRank) <- "diffPopRank"

ChangeinUnempRank <- TiersDF %>%
  group_by(COUNTY) %>%
  arrange(year) %>%
  reframe(diff(UnempRank))
ChangeinUnempRank <- select(ChangeinUnempRank, "diff(UnempRank)")
colnames(ChangeinUnempRank) <- "diffUnempRank"



##the below lists variance of other 3 factors
OtherVariance <- bind_cols(ChangeinIncomeRank, ChangeinPopRank, ChangeinUnempRank)
#now associate it with the master DF, so we can chart variance with years/counties
ChangedPropRank <- bind_cols(OtherVariance, ChangedPropRank)

Variance in

  1. Income Rank
  2. Population Rank
  3. Unemployment Rank

For easy visual comparisons with previous plots, I have grouped the counties according to latest revaluation year, even though Revaluations most certainly do not affect these other 3 factors

##IncomeRank Variance
ChangedPropRank %>%
  filter(LatestReval > 2019) %>%
  ggplot(aes(year, diffIncomeRank, color = COUNTY))+
  geom_line()+
  facet_wrap(~LatestReval)+
  theme(legend.position="none")

quantile(ChangedPropRank$diffIncomeRank)
##   0%  25%  50%  75% 100% 
##  -29   -5    0    5   29
##PopulationRank Variance
ChangedPropRank %>% 
  filter(LatestReval > 2019) %>%
  ggplot(aes(year, diffPopRank, color = COUNTY))+
  geom_line()+
  facet_wrap(~LatestReval)+
  theme(legend.position="none")

quantile(ChangedPropRank$diffPopRank)
##   0%  25%  50%  75% 100% 
##  -45   -5    0    5   37
##UnemploymentRank Variance
ChangedPropRank %>% 
  filter(LatestReval > 2019) %>%
  ggplot(aes(year, diffUnempRank, color = COUNTY))+
  geom_line()+
  facet_wrap(~LatestReval)+
  theme(legend.position="none")

quantile(ChangedPropRank$diffUnempRank)
##   0%  25%  50%  75% 100% 
##  -77   -5    0    6   57

Conclusion

Through this analysis we can see that the YoY variance in rankings is lowest when considering Property Value Rankings (regardless of whether it is a revaluation year or not), with an interquartile range of -1,1; while all other factors have an interquartile range of at least -5, 5. In the end, we find that Property Revaluations are not the driver of YoY change in Economic Distress rankings and their resultant Tier Ranks

It is also obvious that the County Tier system could be overhaulded to more accurately reflect actual economic distress. Ex. High population growth does not mean a county is in less need of state assistnace.

As we saw in the “Background” section, the Tiers ranking system went thought a significant evolution in 2018, shedding its most obvious log-rolling characteristics, another significant evolution is perhaps benefitial in the years to come.